package main

import (
	"encoding/json"
	"fmt"
	"log"
	"math"
	"os"
	"path/filepath"
	"strings"
	"time"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/canvas"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/dialog"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
)

type CalendarApp struct {
	App     fyne.App
	Notes   map[string]string // Key: Date (YYYY-MM-DD), Value: Note
	Buttons map[string]*widget.Button
	Current time.Time // Track the currently displayed month
}

var notesFile string

func getNotesFilePath() string {
	// 使用 Fyne 提供的存储路径
	storagePath := fyne.CurrentApp().Storage().RootURI().Path()
	filePath := filepath.Join(storagePath, "notes.json")
	return filePath
}
func main() {
	myApp := app.NewWithID("com.example.calendar")

	// 得到持久化文件路径
	storagePath := myApp.Storage().RootURI().Path()
	notesFile = filepath.Join(storagePath, "notes.json")
	log.Println(notesFile)

	calendar := &CalendarApp{
		App:     myApp,
		Notes:   make(map[string]string),
		Buttons: make(map[string]*widget.Button),
		Current: time.Now(), // Initialize with the current date
	}
	// Load notes from file at startup
	calendar.loadNotes()

	win := myApp.NewWindow("Calendar")
	content := calendar.buildCalendar(win)
	win.SetContent(content)
	win.Resize(fyne.NewSize(400, 500)) // Increase height to accommodate the new row

	// 定时器：每分钟刷新一次界面
	go func() {
		ticker := time.NewTicker(1 * time.Minute) // 每分钟刷新一次
		defer ticker.Stop()

		for range ticker.C {
			// 重新构建日历并刷新界面
			win.SetContent(calendar.buildCalendar(win))
		}
	}()

	win.ShowAndRun()
}

func saveNotesToFile(filePath string, notes map[string]string) error {
	file, err := os.Create(filePath)
	if err != nil {
		log.Println("Error creating file:", err)
		return err
	}
	defer file.Close()

	encoder := json.NewEncoder(file)
	encoder.SetIndent("", "  ")
	if err := encoder.Encode(notes); err != nil {
		log.Println("Error writing notes to file:", err)
		return err
	}
	log.Println("Notes saved successfully to:", filePath)
	return nil
}

func loadNotesFromFile(filePath string) (map[string]string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		if os.IsNotExist(err) {
			log.Println("File does not exist, initializing empty notes.")
			return make(map[string]string), nil
		}
		log.Println("Error opening file:", err)
		return nil, err
	}
	defer file.Close()

	var notes map[string]string
	decoder := json.NewDecoder(file)
	if err := decoder.Decode(&notes); err != nil {
		log.Println("Error decoding notes from file:", err)
		return nil, err
	}
	log.Println("Notes loaded successfully from:", filePath)
	return notes, nil
}
func (c *CalendarApp) loadNotes() {
	filePath := getNotesFilePath()
	notes, err := loadNotesFromFile(filePath)
	if err != nil {
		log.Println("Failed to load notes:", err)
	} else {
		c.Notes = notes
	}
}

func (c *CalendarApp) saveNotes() {
	filePath := getNotesFilePath()
	if err := saveNotesToFile(filePath, c.Notes); err != nil {
		log.Println("Failed to save notes:", err)
	}
}
func (c *CalendarApp) buildCalendar(win fyne.Window) fyne.CanvasObject {
	now := c.Current // Use the tracked month

	// 动态获取当前日期
	today := time.Now()

	// 中文星期映射
	weekdayMap := map[string]string{
		"Monday":    "星期一",
		"Tuesday":   "星期二",
		"Wednesday": "星期三",
		"Thursday":  "星期四",
		"Friday":    "星期五",
		"Saturday":  "星期六",
		"Sunday":    "星期日",
	}
	currentWeekday := weekdayMap[today.Weekday().String()]
	currentDate := today.Format("2006年01月02日") // Today's date (e.g., "2023年10月05日")

	// Create two text labels for the title
	titleWeekday := canvas.NewText(currentWeekday, theme.DisabledColor()) // Gray color
	titleDate := canvas.NewText(currentDate, theme.DisabledColor())       // Gray color
	titleWeekday.TextStyle.Bold = true                                    // Make weekday bold
	titleDate.TextStyle.Bold = true                                       // Make date bold

	// Center-align the title content
	titleContainer := container.NewVBox(
		container.NewCenter(titleWeekday),
		container.NewCenter(titleDate),
	)

	// Month navigation row
	monthLabel := canvas.NewText(now.Format("01月"), theme.PrimaryColor())
	monthLabel.TextStyle.Bold = true
	leftArrow := widget.NewButton("<", func() {
		c.Current = c.Current.AddDate(0, -1, 0) // Move to the previous month
		win.SetContent(c.buildCalendar(win))    // Rebuild the calendar
	})
	rightArrow := widget.NewButton(">", func() {
		c.Current = c.Current.AddDate(0, 1, 0) // Move to the next month
		win.SetContent(c.buildCalendar(win))   // Rebuild the calendar
	})

	// Ensure the month label is centered and fills the row
	monthNavigation := container.NewBorder(
		nil, nil, leftArrow, rightArrow, // Left and right arrows
		container.NewCenter(monthLabel), // Centered month label
	)

	year, month, _ := now.Date()
	firstDay := time.Date(year, month, 1, 0, 0, 0, 0, time.Local)
	firstWeekday := int(firstDay.Weekday()) // Sunday is 0
	if firstWeekday == 0 {
		firstWeekday = 7 // Adjust to Monday as 1
	}
	totalDays := time.Date(year, month+1, 0, 0, 0, 0, 0, time.Local).Day()

	rows := int(math.Ceil(float64(totalDays+firstWeekday-1) / 7))
	start := firstDay.AddDate(0, 0, -(firstWeekday - 1))

	var buttons []fyne.CanvasObject
	daysOfWeek := []string{"一", "二", "三", "四", "五", "六", "日"} // 中文星期

	// Create the title row using GridWithColumns for alignment
	titleRow := make([]fyne.CanvasObject, len(daysOfWeek))
	for i, day := range daysOfWeek {
		label := widget.NewLabel(day) // 添加"星期"前缀
		label.Alignment = fyne.TextAlignCenter
		titleRow[i] = container.NewCenter(label) // Center-align each label
	}

	// Define a fixed size for all buttons
	fixedSize := fyne.NewSize(50, 50) // Width and height of each cell

	for row := 0; row < rows; row++ {
		for col := 0; col < 7; col++ {
			currentDate := start.AddDate(0, 0, row*7+col)
			key := currentDate.Format("2006-01-02")
			inCurrentMonth := currentDate.Month() == month && currentDate.Year() == year

			text := fmt.Sprintf("%.2d", currentDate.Day())
			btn := widget.NewButton(text, func() {
				if inCurrentMonth {
					note := c.Notes[key]
					entry := widget.NewEntry()
					entry.SetText(note)

					form := container.NewVBox(
						widget.NewLabel("Note:"),
						entry,
					)

					dialog.NewCustom(
						"Edit Note",
						"OK",
						form,
						win,
					).Show()

					entry.OnChanged = func(value string) {
						if value != "" {
							c.Notes[key] = value
							c.updateButton(currentDate)
							c.saveNotes() // Save notes to file when updated
						} else {
							delete(c.Notes, key)
							c.updateButton(currentDate)
							c.saveNotes() // Save notes to file when updated
						}
					}
				}
			})

			// Check if this is today's date
			isToday := currentDate.Year() == today.Year() &&
				currentDate.Month() == today.Month() &&
				currentDate.Day() == today.Day()

			// Create a custom button style for today's date
			if isToday && inCurrentMonth {
				btn.Importance = widget.HighImportance // Use theme's primary color
			} else {
				btn.Importance = widget.MediumImportance // Reset importance
			}

			btn.Alignment = widget.ButtonAlignCenter
			btn.Resize(fixedSize) // Set fixed size for the button
			if !inCurrentMonth {
				btn.Disable()
			}

			hasNote := c.Notes[key] != ""
			if hasNote {
				btn.SetText(text + "•")
			} else {
				btn.SetText(text)
			}

			buttons = append(buttons, container.NewCenter(btn)) // Center-align each button
			c.Buttons[key] = btn
		}
	}

	// Use GridWithColumns for both title and calendar to ensure alignment
	calendarTitle := container.NewGridWithColumns(7, titleRow...)
	calendarBody := container.NewGridWithColumns(7, buttons...)

	// Combine the title, month navigation, and calendar into a single layout
	return container.NewVBox(
		titleContainer,  // Dynamic title
		monthNavigation, // Month navigation row
		calendarTitle,   // Weekday headers
		calendarBody,    // Calendar dates
	)
}
func (c *CalendarApp) updateButton(date time.Time) {
	key := date.Format("2006-01-02")
	btn := c.Buttons[key]
	if btn == nil {
		return
	}

	hasNote := c.Notes[key] != ""
	text := btn.Text
	baseText := strings.TrimSuffix(text, "•")

	if hasNote {
		btn.SetText(baseText + "•")
	} else {
		btn.SetText(baseText)
	}
}
